// ServerFunctions.cpp : Implements the server-side wrapped RPC functions
//

#include "pch.h"
#include "RootRpcServer.h"

WINERR RootRpcCopyFile(
	/* [string][ref][in] */ LPCWSTR lpExistingFileName,
	/* [string][ref][in] */ LPCWSTR lpNewFileName,
	/* [in] */ BOOL bFailIfExists)
{
	if (!::CopyFileW(lpExistingFileName, lpNewFileName, bFailIfExists))
	{
		return ::GetLastError();
	}
	return ERROR_SUCCESS;
}

HRESULT RootRpcCopyFile2(
	/* [string][ref][in] */ LPCWSTR pwszExistingFileName,
	/* [string][ref][in] */ LPCWSTR pwszNewFileName,
	/* [unique][in] */ ROOTRPC_COPYFILE2_EXTENDED_PARAMETERS *pExtendedParameters)
{
	if (pExtendedParameters)
	{
		if (pExtendedParameters->pfCancel || pExtendedParameters->pProgressRoutine)
		{	// We don't support cancelation or callbacks at this time.
			return ERROR_NOT_CAPABLE;
		}
		COPYFILE2_EXTENDED_PARAMETERS ep;
		ep.dwSize = pExtendedParameters->dwSize;
		ep.dwCopyFlags = pExtendedParameters->dwCopyFlags;
		ep.pfCancel = nullptr;
		ep.pProgressRoutine = nullptr;
		ep.pvCallbackContext = nullptr;
		return ::CopyFile2(pwszExistingFileName, pwszNewFileName, &ep);
	}
	return ::CopyFile2(pwszExistingFileName, pwszNewFileName, nullptr);
}

WINERR RootRpcCreateFile(
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [in] */ DWORD dwDesiredAccess,
	/* [in] */ DWORD dwShareMode,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	/* [in] */ DWORD dwCreationDisposition,
	/* [in] */ DWORD dwFlagsAndAttributes,
	/* [in] */ HANDLE hTemplateFile,
	/* [out] */ PHANDLE returnval)
{
	//if (lpSecurityAttributes)
	//{	// There's a security attributes struct, but its format is a little off
	//	SECURITY_ATTRIBUTES sa;
	//	sa.nLength = lpSecurityAttributes->nLength;
	//	sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	//	sa.lpSecurityDescriptor = nullptr;
	//	*returnval = ::CreateFileW(
	//		lpFileName,
	//		dwDesiredAccess,
	//		dwShareMode,
	//		&sa,
	//		dwCreationDisposition,
	//		dwFlagsAndAttributes,
	//		hTemplateFile);
	//}
	//else
	{
		*returnval = ::CreateFileW(
			lpFileName,
			dwDesiredAccess,
			dwShareMode,
			reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes),
			dwCreationDisposition,
			dwFlagsAndAttributes,
			hTemplateFile);
	}
	return ::GetLastError();
}

WINERR RootRpcCreateFile2(
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [in] */ DWORD dwDesiredAccess,
	/* [in] */ DWORD dwShareMode,
	/* [in] */ DWORD dwCreationDisposition,
	/* [unique][in] */ ROOTRPC_LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams,
	/* [out] */ PHANDLE returnval)
{
	//SECURITY_ATTRIBUTES sa;
	//CREATEFILE2_EXTENDED_PARAMETERS ep;
	//if (pCreateExParams)
	//{
	//	if (pCreateExParams->lpSecurityAttributes)
	//	{	// There's a security attributes struct, but its format is a little off
	//		sa.nLength = pCreateExParams->lpSecurityAttributes->nLength;
	//		sa.bInheritHandle = pCreateExParams->lpSecurityAttributes->bInheritHandle;
	//		sa.lpSecurityDescriptor = nullptr;
	//		ep.lpSecurityAttributes = &sa;
	//	}
	//	else
	//	{
	//		// No security attributes passed in at all.
	//		ep.lpSecurityAttributes = nullptr;
	//	}
	//	// Copy the extended parameters from the "fake" ROOTRPC struct to the real one.
	//	ep.dwSize = pCreateExParams->dwSize;
	//	ep.dwFileAttributes = pCreateExParams->dwFileAttributes;
	//	ep.dwFileFlags = pCreateExParams->dwFileFlags;
	//	ep.dwSecurityQosFlags = pCreateExParams->dwSecurityQosFlags;
	//	ep.hTemplateFile = pCreateExParams->hTemplateFile;
	//}
	*returnval = ::CreateFile2(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		dwCreationDisposition,
		//pCreateExParams ? &ep : nullptr);
		reinterpret_cast<LPCREATEFILE2_EXTENDED_PARAMETERS>(pCreateExParams));
	return ::GetLastError();
}

WINERR RootRpcCreateHardLink(
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [string][ref][in] */ LPCWSTR lpExistingFileName,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	//SECURITY_ATTRIBUTES sa;
	//if (lpSecurityAttributes)
	//{
	//	// There's a security attributes struct, but its format is a little off
	//	sa.nLength = lpSecurityAttributes->nLength;
	//	sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	//	sa.lpSecurityDescriptor = nullptr;
	//}
	if (::CreateHardLinkW(
		lpFileName,
		lpExistingFileName,
		//lpSecurityAttributes ? &sa : nullptr));
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes)))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcCreateSymbolicLink(
	/* [string][ref][in] */ LPWSTR lpSymlinkFileName,
	/* [string][ref][in] */ LPWSTR lpTargetFileName,
	/* [in] */ DWORD dwFlags)
{
	if (::CreateSymbolicLinkW(lpSymlinkFileName, lpTargetFileName, dwFlags))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcDeleteFile(
	/* [string][ref][in] */ LPCWSTR lpFileName)
{
	if (::DeleteFileW(lpFileName))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcFindClose(
	/* [out][in] */ FINDHANDLE *hFindFile)
{
	WINERR err = NO_ERROR;
	if (!::FindClose(*hFindFile))
	{
		err = ::GetLastError();
	}
	// We need to return NULL so the runtime knows the context handle is closed.
	*hFindFile = nullptr;
	return err;
}

WINERR RootRpcFindFirstFile(
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [out] */ LPWIN32_FIND_DATAW lpFindFileData,
	/* [out] */ FINDHANDLE *findhandle)
{
	WINERR err = NO_ERROR;
	::LogVerbose(L"RootRpcFindFirstFile called with filename \"%s\".\n", lpFileName);
	*findhandle = ::FindFirstFileW(lpFileName, lpFindFileData);
	if (INVALID_HANDLE_VALUE == *findhandle)
	{
		// Ugh, context handles and invalid values don't get along.
		err = ::GetLastError();
		::LogWarning(L"FindFirstFileW returned INVALID_HANDLE_VALUE, Win32 error %d.\n", err);
		*findhandle = nullptr;
		return err;
	}
	if (!*findhandle)
	{
		// The function returned a NULL pointer. This *might* not be a failure...?
		// Worth checking GetLastError here, just in case it *is* an error.
		err = ::GetLastError();
		::LogWarning(L"FindFirstFileExW returned NULL, Win32 error %d.\n", ::GetLastError());
		// We can't transmit it as NULL; the runtime will think the context handle is closed.
		*findhandle = INVALID_HANDLE_VALUE;
		return err;
	}
	::LogVerbose(L"FindFirstFileW returned %p.\n", *findhandle);
	return err;
}

WINERR RootRpcFindFirstFileEx(
    /* [string][ref][in] */ LPCWSTR lpFileName,
    /* [in] */ FINDEX_INFO_LEVELS fInfoLevelId,
    /* [out] */ PWIN32_FIND_DATAW lpFindFileData,
    /* [in] */ FINDEX_SEARCH_OPS fSearchOp,
    /* [in] */ RPC_PTR lpSearchFilter,
    /* [in] */ DWORD dwAdditionalFlags,
    /* [out] */ FINDHANDLE *findhandle)
{
	WINERR err = NO_ERROR;
	::LogVerbose(L"RootRpcFindFirstFileEx called with filename \"%s\".\n", lpFileName);
	*findhandle = ::FindFirstFileExW(
		lpFileName,			// Search location and wildcard pattern
		fInfoLevelId,		// Standard or Basic. Other values may fail.
		lpFindFileData,		// Receives first file's info. Type may change in future versions.
		fSearchOp,			// Any name match or directory-only. May in future allow device-only.
							// Parameter reserved for future operations that need more data.
		reinterpret_cast<LPVOID>(lpSearchFilter),
		dwAdditionalFlags);	// Case-sensitive and/or large buffer.
	if (INVALID_HANDLE_VALUE == *findhandle)
	{
		err = ::GetLastError();
		// Ugh. We have to set the context handle to NULL so the runtime knows it's closed.
		::LogWarning(L"FindFirstFileExW returned INVALID_HANDLE_VALUE, Win32 error %d.\n", err);
		*findhandle = nullptr;
		return err;
	}
	if (!*findhandle)
	{
		// The function returned a NULL pointer. This *might* not be a failure...?
		// Worth checking GetLastError here, just in case it *is* an error.
		err = ::GetLastError();
		::LogWarning(L"FindFirstFileExW returned NULL, Win32 error %d.\n", ::GetLastError());
		// We can't transmit it as NULL; the runtime will think the context handle is closed.
		*findhandle = INVALID_HANDLE_VALUE;
		return err;
	}
	::LogVerbose(L"FindFirstFileExW returned %p.\n", *findhandle);
	return err;
}

WINERR RootRpcFindNextFile(
	/* [in] */ FINDHANDLE hFindFile,
	/* [out] */ LPWIN32_FIND_DATAW lpFindFileData)
{
	if (INVALID_HANDLE_VALUE == hFindFile)
	{
		// It's not clear whether NULL is a valid return value from FindFirstFile, etc.
		// It's illegal for a context pointer, though, so INVALID_HANDLE_VALUE is used instead.
		if (!::FindNextFileW(nullptr, lpFindFileData))
		{
			// The function failed.
			return ::GetLastError();
		}
	}
	else
	{
		if (!::FindNextFileW(hFindFile, lpFindFileData))
		{
			// The function failed.
			return ::GetLastError();
		}
	}
	return NO_ERROR;
}

WINERR RootRpcGetFileAttributes(
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [out] */ PDWORD attributes)
{
	*attributes = ::GetFileAttributesW(lpFileName);
	if (INVALID_FILE_ATTRIBUTES == *attributes)
	{
		// The function failed.
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcGetFileAttributesEx(
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [in] */ GET_FILEEX_INFO_LEVELS fInfoLevelId,
	/* [out] */ LPWIN32_FILE_ATTRIBUTE_DATA lpFileInformation)
{
	if (!::GetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcMoveFile(
	/* [string][ref][in] */ LPCWSTR lpExistingFileName,
	/* [string][ref][in] */ LPCWSTR lpNewFileName)
{
	if (!::MoveFileW(lpExistingFileName, lpNewFileName))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcMoveFileEx(
	/* [string][ref][in] */ LPCWSTR lpExistingFileName,
	/* [string][unique][in] */ LPCWSTR lpNewFileName,
	/* [in] */ DWORD dwFlags)
{
	if (!::MoveFileExW(lpExistingFileName, lpNewFileName, dwFlags))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcReplaceFile(
	/* [string][ref][in] */ LPCWSTR lpReplacedFileName,
	/* [string][ref][in] */ LPCWSTR lpReplacementFileName,
	/* [string][unique][in] */ LPCWSTR lpBackupFileName,
	/* [in] */ DWORD dwReplaceFlags,
	/* [in] */ RPC_PTR lpExclude,
	/* [in] */ RPC_PTR lpReserved)
{
	if (!::ReplaceFileW(
		lpReplacedFileName,			// Pathname of file to be replaced
		lpReplacementFileName,		// Pathname of the file that replaces the first param
		lpBackupFileName,			// Optional pathname of a backup of the replaced file
		dwReplaceFlags,				// Writethrough, ignore merge errors, ignore ACL errors
		reinterpret_cast<LPVOID>(lpExclude),				// Reserved pointer, not used
		reinterpret_cast<LPVOID>(lpReserved)))				// reserved pointer, not used
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcSearchPath(
	/* [string][unique][in] */ LPCWSTR lpPath,
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [string][unique][in] */ LPCWSTR lpExtension,
	/* [in] */ DWORD nBufferLength,
	/* [size_is][full][out][in] */ LPWSTR lpBuffer,
	/* [full][out][in] */ LPWSTR *lpFilePart,
	/* [out] */ PDWORD returnedchars)
{
	*returnedchars = ::SearchPath(
		lpPath,			// Optional path under which to search
		lpFileName,		// The file name to search for
		lpExtension,	// Optional extension to append to the filename
		nBufferLength,	// The number of characters in the buffer
		lpBuffer,		// The buffer that receives the null-terminated full file pathname
		lpFilePart);	// Optional ptr receives ptr to the start of the file name
	if (!*returnedchars)
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcSetFileAttributes(
	/* [string][ref][in] */ LPCWSTR lpFileName,
	/* [in] */ DWORD dwFileAttributes)
{
	if (!::SetFileAttributesW(lpFileName, dwFileAttributes))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcCreateDirectory(
	/* [string][ref][in] */ LPCWSTR lpPathName,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	//if (lpSecurityAttributes)
	//{	// There's a security attributes struct, but its format is a little off
	//	SECURITY_ATTRIBUTES sa;
	//	sa.nLength = lpSecurityAttributes->nLength;
	//	sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	//	sa.lpSecurityDescriptor = nullptr;
	//	if (!::CreateDirectoryW(lpPathName, &sa))
	//	{
	//		return ::GetLastError();
	//	}
	//}
	//else
	{
		if (!::CreateDirectoryW(
			lpPathName, 
			reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes)))
		{
			return ::GetLastError();
		}
	}
	return NO_ERROR;
}

WINERR RootRpcCreateDirectoryEx(
	/* [string][ref][in] */ LPCWSTR lpTemplateDirectory,
	/* [string][ref][in] */ LPCWSTR lpNewDirectory,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	//if (lpSecurityAttributes)
	//{	// There's a security attributes struct, but its format is a little off
	//	SECURITY_ATTRIBUTES sa;
	//	sa.nLength = lpSecurityAttributes->nLength;
	//	sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	//	sa.lpSecurityDescriptor = nullptr;
	//	if (!::CreateDirectoryExW(lpTemplateDirectory, lpNewDirectory, &sa))
	//	{
	//		return ::GetLastError();
	//	}
	//}
	//else
	//{
		if (!::CreateDirectoryExW(
			lpTemplateDirectory, 
			lpNewDirectory, 
			reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes)))
		{
			return ::GetLastError();
		}
	//}
	return NO_ERROR;
}

WINERR RootRpcFindFirstChangeNotification(
	/* [string][ref][in] */ LPCWSTR lpPathName,
	/* [in] */ BOOL bWatchSubtree,
	/* [in] */ DWORD dwNotifyFilter,
	/* [out] */ PCHANGEHANDLE waithandle)
{
	*waithandle = ::FindFirstChangeNotificationW(lpPathName, bWatchSubtree, dwNotifyFilter);
	if (INVALID_HANDLE_VALUE == *waithandle)
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcRemoveDirectory(
	/* [string][ref][in] */ LPCWSTR lpPathName)
{
	if (!::RemoveDirectoryW(lpPathName))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcDefineDosDevice(
	/* [in] */ DWORD dwFlags,
	/* [string][ref][in] */ LPCWSTR lpDeviceName,
	/* [string][unique][in] */ LPCWSTR lpTargetPath)
{
	if (::DefineDosDeviceW(dwFlags, lpDeviceName, lpTargetPath))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcDeleteVolumeMountPoint(
	/* [string][ref][in] */ LPCWSTR lpszVolumeMountPoint)
{
	if (::DeleteVolumeMountPointW(lpszVolumeMountPoint))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcFindFirstVolume(
	/* [in] */ DWORD cchBufferLength,
	/* [size_is][out] */ LPWSTR lpszVolumeName,
	/* [out] */ FINDVOLHANDLE *findhandle)
{
	if (!cchBufferLength)
	{
		*findhandle = nullptr;
		return ERROR_INVALID_PARAMETER;
	}
	lpszVolumeName[0] = L'\0';
	*findhandle = ::FindFirstVolumeW(lpszVolumeName, cchBufferLength);
	if (INVALID_HANDLE_VALUE == *findhandle)
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcFindNextVolume(
	/* [in] */ FINDVOLHANDLE hFindVolume,
	/* [in] */ DWORD cchBufferLength,
	/* [size_is][out] */ LPWSTR lpszVolumeName)
{
	if (!cchBufferLength)
	{
		return ERROR_INVALID_PARAMETER;
	}
	lpszVolumeName[0] = L'\0';
	if (!::FindNextVolumeW(hFindVolume, lpszVolumeName, cchBufferLength))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcFindVolumeClose(
	/* [out][in] */ FINDVOLHANDLE *hFindVolume)
{
	BOOL b = ::FindVolumeClose(*hFindVolume);
	*hFindVolume = nullptr;
	return b ? NO_ERROR : ::GetLastError();
}

unsigned int RootRpcGetDriveType(
	/* [string][unique][in] */ LPCWSTR lpRootPathName)
{
	return ::GetDriveTypeW(lpRootPathName);
}

WINERR RootRpcGetVolumeInformation(
	/* [string][unique][in] */ LPCWSTR lpRootPathName,
	/* [in] */ DWORD nVolumeNameSize,
	/* [size_is][string][partial_ignore][unique][out][in] */ LPWSTR lpVolumeNameBuffer,
	/* [partial_ignore][unique][out][in] */ LPDWORD lpVolumeSerialNumber,
	/* [partial_ignore][unique][out][in] */ LPDWORD lpMaximumComponentLength,
	/* [partial_ignore][unique][out][in] */ LPDWORD lpFileSystemFlags,
	/* [in] */ DWORD nFileSystemNameSize,
	/* [size_is][string][partial_ignore][unique][out][in] */ LPWSTR lpFileSystemNameBuffer)
{
	if (!::GetVolumeInformationW(
		lpRootPathName,
		lpVolumeNameBuffer,
		nVolumeNameSize,
		lpVolumeSerialNumber,
		lpMaximumComponentLength,
		lpFileSystemFlags,
		lpFileSystemNameBuffer,
		nFileSystemNameSize))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcGetVolumeNameForVolumeMountPoint(
	/* [string][ref][in] */ LPCWSTR lpszVolumeMountPoint,
	/* [in] */ DWORD cchBufferLength,
	/* [size_is][string][out] */ LPWSTR lpszVolumeName)
{
	if (!::GetVolumeNameForVolumeMountPointW(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcGetVolumePathName(
	/* [string][ref][in] */ LPCWSTR lpszFileName,
	/* [in] */ DWORD cchBufferLength,
	/* [size_is][string][out] */ LPWSTR lpszVolumePathName)
{
	if (!::GetVolumePathNameW(lpszFileName, lpszVolumePathName, cchBufferLength))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcGetVolumePathNamesForVolumeName(
	/* [string][ref][in] */ LPCWSTR lpszVolumeName,
	/* [in] */ DWORD cchBufferLength,
	/* [size_is][out] */ LPWSTR lpszVolumePathNames,
	/* [out] */ PDWORD lpcchReturnLength)
{
	if (!::GetVolumePathNamesForVolumeNameW(
		lpszVolumeName,			// GUID path for volume (\\?\Volume{GUID}\)
		lpszVolumePathNames,	// Multi-string (NULL-delimited) of mount points, plus final NULL
		cchBufferLength,		// Length of the VolumePathNames buffer, in wide characters
		lpcchReturnLength))		// Number of chracters returned, or required output buffer size
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcQueryDosDevice(
	/* [string][unique][in] */ LPCWSTR lpDeviceName,
	/* [in] */ DWORD ucchMax,
	/* [size_is][out] */ LPWSTR lpTargetPath,
	/* [out] */ PDWORD returnedbytes)
{
	*returnedbytes = ::QueryDosDeviceW(lpDeviceName, lpTargetPath, ucchMax);
	if (!(*returnedbytes))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcSetVolumeLabel(
	/* [string][unique][in] */ LPCWSTR lpRootPathName,
	/* [string][unique][in] */ LPCWSTR lpVolumeName)
{
	if (!::SetVolumeLabelW(lpRootPathName, lpVolumeName))
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

/* For some reason, this function isn't available in WP8.1!
WINERR RootRpcSetVolumeMountPoint(
*/


LONG RootRpcRegCreateKeyEx(
    /* [in] */ HKEY hKey,
    /* [string][ref][in] */ LPCWSTR lpSubKey,
    /* [in] */ DWORD Reserved,
    /* [string][unique][in] */ LPWSTR lpClass,
    /* [in] */ DWORD dwOptions,
    /* [in] */ REGSAM samDesired,
    /* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    /* [out] */ PHKEY phkResult,
    /* [partial_ignore][unique][out][in] */ LPDWORD lpdwDisposition)
{
	//if (lpSecurityAttributes)
	//{
	//	SECURITY_ATTRIBUTES sa;
	//	sa.nLength = lpSecurityAttributes->nLength;
	//	sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	//	sa.lpSecurityDescriptor = nullptr;
	//	return ::RegCreateKeyExW(
	//		hKey,
	//		lpSubKey,
	//		Reserved,
	//		lpClass,
	//		dwOptions,
	//		samDesired,
	//		&sa,
	//		phkResult,
	//		lpdwDisposition);
	//}
	return ::RegCreateKeyExW(
		hKey,
		lpSubKey,
		Reserved,
		lpClass,
		dwOptions,
		samDesired,
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes),
		phkResult,
		lpdwDisposition);
}

LONG RootRpcRegDeleteKey(
	/* [in] */ HKEY hKey,
	/* [string][ref][in] */ LPCWSTR lpSubKey)
{
	return ::RegDeleteKeyW(hKey, lpSubKey);
}

LONG RootRpcRegDeleteKeyEx(
	/* [in] */ HKEY hKey,
	/* [string][ref][in] */ LPCWSTR lpSubKey,
	/* [in] */ REGSAM samDesired,
	/* [in] */ DWORD Reserved)
{
	return ::RegDeleteKeyExW(hKey, lpSubKey, samDesired, Reserved);
}

LONG RootRpcRegDeleteTree(
	/* [in] */ HKEY hKey,
	/* [string][unique][in] */ LPCWSTR lpSubKey)
{
	return ::RegDeleteTreeW(hKey, lpSubKey);
}

LONG RootRpcRegLoadKey(
	/* [in] */ HKEY hKey,
	/* [string][unique][in] */ LPCWSTR lpSubKey,
	/* [string][ref][in] */ LPCWSTR lpFile)
{
	return ::RegLoadKeyW(hKey, lpSubKey, lpFile);
}

LONG RootRpcRegOpenCurrentUser(
	/* [in] */ REGSAM samDesired,
	/* [out] */ PHKEY phkResult)
{
	// This function is used to get the HKCU hive for the RPC client.
	// It's supposed to get it for the impersonated user, so lets impersonate the caller.
	LONG res;
	RPC_STATUS stat;
/*	// This is an attempt at implementing the complicated, manual version. Not tested yet.
	DWORD pid = 0x0;
	WINERR err;
	HANDLE proc, token, dupe;
	// Start by getting the PID of the calling process.
	stat = I_RpcBindingInqLocalClientPID(nullptr, &pid);
	if (stat)
	{
		*phkResult = nullptr;
		return stat;
	}
	// Open a HANDLE to the calling process, so we can get its token.
	proc = ::OpenProcess(PROCESS_ALL_ACCESS, false, pid);
	if (!proc)
	{
		*phkResult = nullptr;
		return ::GetLastError();
	}
	// Get a HANDLE to the security token of the calling process.
	if (!::OpenProcessToken(proc, TOKEN_ALL_ACCESS, &token))
	{
		// Failed to get the token HANDLE
		err = ::GetLastError();
		::CloseHandle(proc);
		*phkResult = nullptr;
		return err;
	}
	// Duplicate the process token to turn it into an impersonation token.
	if (!::DuplicateToken(token, SecurityImpersonation, &dupe))
	{
		// Failed to duplicate the existing token.
		err = ::GetLastError();
		::CloseHandle(token);
		::CloseHandle(proc);
		*phkResult = nullptr;
		return err;
	}
	// Impersonate the calling process so we can easily open its Current User registry hive.
	if (!::SetThreadToken(nullptr, dupe))
	{
		// Unable to impersonate for some reason.
		err = ::GetLastError();
		::CloseHandle(dupe);
		::CloseHandle(token);
		::CloseHandle(proc);
		*phkResult = nullptr;
		return err;
	}
	// OK, we are finally impersonating the caller. Let's do this call...
	res = ::RegOpenCurrentUser(samDesired, phkResult);
	// Don't need to bother checking whether the call succeeded, really. Just clean up and return.
	// First, revert from impersonation.
	::RevertToSelf();
	// Close our unneeded HANDLEs.
	::CloseHandle(dupe);
	::CloseHandle(token);
	::CloseHandle(proc);
	*/
	// OK, first impersonate the calling process.
	stat = ::RpcImpersonateClient(nullptr);
	if (stat)
	{
		// Impersonation failed for some reason. Maybe the client doesn't allow it?
		// We could then try getting the process token and working with that, but... so much work.
		*phkResult = nullptr;
		return stat;
	}
	// OK, we are impersonating the caller. Time to call the actual API...
	res = ::RegOpenCurrentUser(samDesired, phkResult);
	// Don't need to bother checking whether the call succeeded, really. Just clean up and return.
	// First, revert from impersonation.
	stat = RpcRevertToSelf();
	if (stat)
	{
		// The revert failed, really? Well hell, what to do now? I guess return the error code...
		return stat;
	}
	return res;
}

LONG RootRpcRegOpenKeyEx(
	/* [in] */ HKEY hKey,
	/* [string][unique][in] */ LPCWSTR lpSubKey,
	/* [in] */ DWORD ulOptions,
	/* [in] */ REGSAM samDesired,
	/* [out] */ PHKEY phkResult)
{
	return ::RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
}

LONG RootRpcRegOpenUserClassesRoot(
	/* [in] */ HANDLE hToken,
	/* [in] */ DWORD dwOptions,
	/* [in] */ REGSAM samDesired,
	/* [out] */ PHKEY phkResult)
{
	// Probably mainly used to get the HKCR combined view of the client with more access.
	// We will assume the token HANDLE is for the caller (or whoever they want, really).
	return ::RegOpenUserClassesRoot(hToken, dwOptions, samDesired, phkResult);
}

LONG RootRpcRegReplaceKey(
	/* [in] */ HKEY hKey,
	/* [string][unique][in] */ LPCWSTR lpSubKey,
	/* [string][ref][in] */ LPCWSTR lpNewFile,
	/* [string][ref][in] */ LPCWSTR lpOldFile)
{
	return ::RegReplaceKeyW(hKey, lpSubKey, lpNewFile, lpOldFile);
}

LONG RootRpcRegRestoreKey(
	/* [in] */ HKEY hKey,
	/* [string][ref][in] */ LPCWSTR lpFile,
	/* [in] */ DWORD dwFlags)
{
	return ::RegRestoreKeyW(hKey, lpFile, dwFlags);
}

LONG RootRpcRegSaveKey(
	/* [in] */ HKEY hKey,
	/* [string][ref][in] */ LPCWSTR lpFile,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	return ::RegSaveKeyW(
		hKey, 
		lpFile, 
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes));
}

LONG RootRpcRegSaveKeyEx(
	/* [in] */ HKEY hKey,
	/* [string][ref][in] */ LPCWSTR lpFile,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	/* [in] */ DWORD Flags)
{
	return ::RegSaveKeyExW(
		hKey, 
		lpFile, 
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes), 
		Flags);
}

LONG RootRpcRegUnLoadKey(
	/* [in] */ HKEY hKey,
	/* [string][unique][in] */ LPCWSTR lpSubKey)
{
	return ::RegUnLoadKeyW(hKey, lpSubKey);
}

WINERR RootRpcCreateJobObject(
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	/* [string][unique][in] */ LPCWSTR lpName,
	/* [out] */ PHANDLE result)
{
	//SECURITY_ATTRIBUTES sa;
	//if (lpSecurityAttributes)
	//{	// There's a security attributes struct, but its format is a little off
	//	sa.nLength = lpSecurityAttributes->nLength;
	//	sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	//	sa.lpSecurityDescriptor = nullptr;
	//}
	*result = ::CreateJobObjectW(
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes), 
		lpName);
	// Even on success, we will sometimes return a value (like ERROR_ALREADY_EXISTS).
	return ::GetLastError();
}

WINERR RootRpcCreateProcess(
	/* [string][unique][in] */ LPCWSTR lpApplicationName,
	/* [string][unique][out][in] */ LPWSTR lpCommandLine,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpProcessAttributes,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpThreadAttributes,
	/* [in] */ BOOL bInheritHandles,
	/* [in] */ DWORD dwCreationFlags,
	/* [in] */ DWORD cbE,
	/* [size_is][unique][in] */ PBYTE lpEnvironment,
	/* [string][unique][in] */ LPCWSTR lpCurrentDirectory,
	/* [in] */ LPSTARTUPINFOW lpStartupInfo,
	/* [out] */ LPPROCESS_INFORMATION lpProcessInformation)
{
	//SECURITY_ATTRIBUTES pa, ta;
	// Check for some options we don't (currently) support.
	if (bInheritHandles ||
		(dwCreationFlags & EXTENDED_STARTUPINFO_PRESENT))
	{
		lpProcessInformation->hProcess = INVALID_HANDLE_VALUE;
		lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
		return ERROR_NOT_CAPABLE;
	}
	//if (lpProcessAttributes)
	//{
	//	pa.bInheritHandle = lpProcessAttributes->bInheritHandle;
	//	pa.nLength = lpProcessAttributes->nLength;
	//	pa.lpSecurityDescriptor = nullptr;
	//}
	//if (lpThreadAttributes)
	//{
	//	ta.bInheritHandle = lpThreadAttributes->bInheritHandle;
	//	ta.nLength = lpThreadAttributes->nLength;
	//	ta.lpSecurityDescriptor = nullptr;
	//}
	if (::CreateProcessW(
		lpApplicationName,
		lpCommandLine,
		//lpProcessAttributes ? &pa : nullptr,
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpProcessAttributes),
		//lpThreadAttributes ? &ta : nullptr,
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpThreadAttributes),
		bInheritHandles,						// Always FALSE for now
		dwCreationFlags,
		lpEnvironment,
		lpCurrentDirectory,
		lpStartupInfo,
		lpProcessInformation))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcCreateProcessAsUser(
	/* [in] */ HANDLE hToken,
	/* [string][unique][in] */ LPCWSTR lpApplicationName,
	/* [string][unique][out][in] */ LPWSTR lpCommandLine,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpProcessAttributes,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpThreadAttributes,
	/* [in] */ BOOL bInheritHandles,
	/* [in] */ DWORD dwCreationFlags,
	/* [in] */ DWORD cbE,
	/* [size_is][unique][in] */ PBYTE lpEnvironment,
	/* [string][unique][in] */ LPCWSTR lpCurrentDirectory,
	/* [in] */ LPSTARTUPINFOW lpStartupInfo,
	/* [out] */ LPPROCESS_INFORMATION lpProcessInformation)
{
	//SECURITY_ATTRIBUTES pa, ta;
	// Check for some options we don't (currently) support.
	if (bInheritHandles ||
		(dwCreationFlags & EXTENDED_STARTUPINFO_PRESENT))
	{
		lpProcessInformation->hProcess = INVALID_HANDLE_VALUE;
		lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
		return ERROR_NOT_CAPABLE;
	}
	//if (lpProcessAttributes)
	//{
	//	pa.bInheritHandle = lpProcessAttributes->bInheritHandle;
	//	pa.nLength = lpProcessAttributes->nLength;
	//	pa.lpSecurityDescriptor = nullptr;
	//}
	//if (lpThreadAttributes)
	//{
	//	ta.bInheritHandle = lpThreadAttributes->bInheritHandle;
	//	ta.nLength = lpThreadAttributes->nLength;
	//	ta.lpSecurityDescriptor = nullptr;
	//}
	if (::CreateProcessAsUserW(
		hToken,
		lpApplicationName,
		lpCommandLine,
		//lpProcessAttributes ? &pa : nullptr,
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpProcessAttributes),
		//lpThreadAttributes ? &ta : nullptr,
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpThreadAttributes),
		bInheritHandles,						// Always FALSE for now
		dwCreationFlags,
		lpEnvironment,
		lpCurrentDirectory,
		lpStartupInfo,
		lpProcessInformation))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcEnumProcesses(
	/* [in] */ DWORD cb,
	/* [length_is][size_is][out] */ PDWORD pProcessIds,
	/* [out] */ PDWORD pBytesReturned)
{
	if (::EnumProcesses(pProcessIds, cb, pBytesReturned))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcOpenJobObject(
	/* [in] */ DWORD dwDesiredAccess,
	/* [in] */ BOOL bInheritHandle,
	/* [string][in] */ LPCWSTR lpName,
	/* [out] */ PHANDLE result)
{
	*result = ::OpenJobObjectW(dwDesiredAccess, bInheritHandle, lpName);
	if (*result)
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcOpenProcess(
	/* [in] */ DWORD dwDesiredAccess,
	/* [in] */ BOOL bInheritHandle,
	/* [in] */ DWORD dwProcessId,
	/* [out] */ PHANDLE result)
{
	*result = ::OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
	if (*result)
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcOpenThread(
	/* [in] */ DWORD dwDesiredAccess,
	/* [in] */ BOOL bInheritHandle,
	/* [in] */ DWORD dwThreadId,
	/* [out] */ PHANDLE result)
{
	*result = ::OpenThread(dwDesiredAccess, bInheritHandle, dwThreadId);
	if (*result)
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcCallNamedPipe(
	/* [string][in] */ LPCWSTR lpNamedPipeName,
	/* [in] */ DWORD nInBufferSize,
	/* [size_is][in] */ LPBYTE lpInBuffer,
	/* [in] */ DWORD nOutBufferSize,
	/* [size_is][out] */ LPBYTE lpOutBuffer,
	/* [out] */ LPDWORD lpBytesRead,
	/* [in] */ DWORD nTimeOut)
{
	// CallNamedPipe doesn't seem to be available.
	// So, fake it by calling its component functions instead.
	WINERR err = NO_ERROR;
	HANDLE pipe = CreateFileW(
		lpNamedPipeName,
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		nullptr,
		OPEN_EXISTING,
		0x0,
		nullptr);
	if (!pipe || INVALID_HANDLE_VALUE == pipe)
	{
		err = ::GetLastError();
		if (ERROR_PIPE_BUSY != err)
		{
			*lpBytesRead = 0x0;
			return err;
		}
		if (::WaitNamedPipeW(lpNamedPipeName, nTimeOut))
		{
			// Pipe is available now (unless it vainshes between check and use).
			pipe = CreateFileW(
				lpNamedPipeName,
				GENERIC_READ | GENERIC_WRITE,
				FILE_SHARE_READ | FILE_SHARE_WRITE,
				nullptr,
				OPEN_EXISTING,
				0x0,
				nullptr);
		}
		if (!pipe || INVALID_HANDLE_VALUE == pipe)
		{
			// Either the wait failed to get a pipe, or we couldn't connect to it.
			return ::GetLastError();
		}
	}
	// Pipe is open. Let's do the transaction.
	if (::TransactNamedPipe(
		pipe,
		lpInBuffer,
		nInBufferSize,
		lpOutBuffer,
		nOutBufferSize,
		lpBytesRead,
		nullptr))
	{
		err = NO_ERROR;
	}
	else
	{
		err = ::GetLastError();
	}
	::CloseHandle(pipe);
	return err;
}

WINERR RootRpcCreateNamedPipe(
	/* [string][in] */ LPCWSTR lpName,
	/* [in] */ DWORD dwOpenMode,
	/* [in] */ DWORD dwPipeMode,
	/* [in] */ DWORD nMaxInstances,
	/* [in] */ DWORD nOutBufferSize,
	/* [in] */ DWORD nInBufferSize,
	/* [in] */ DWORD nDefaultTimeOut,
	/* [unique][in] */ ROOTRPC_LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	/* [out] */ PHANDLE result)
{
	//SECURITY_ATTRIBUTES sa;
	//if (lpSecurityAttributes)
	//{
	//	sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	//	sa.nLength = lpSecurityAttributes->nLength;
	//	sa.lpSecurityDescriptor = nullptr;
	//}
	*result = ::CreateNamedPipeW(
		lpName,
		dwOpenMode,
		dwPipeMode,
		nMaxInstances,
		nOutBufferSize,
		nInBufferSize,
		nDefaultTimeOut,
		//lpSecurityAttributes ? &sa : nullptr);
		reinterpret_cast<LPSECURITY_ATTRIBUTES>(lpSecurityAttributes));
	if (INVALID_HANDLE_VALUE == *result)
	{
		return ::GetLastError();
	}
	return NO_ERROR;
}

WINERR RootRpcWaitNamedPipe(
	/* [string][in] */ LPCWSTR lpNamedPipeName,
	/* [in] */ DWORD nTimeOut)
{
	if (::WaitNamedPipeW(lpNamedPipeName, nTimeOut))
	{
		// Pipe is available (no error and didn't time out).
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcChangeServiceConfig(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ DWORD dwServiceType,
	/* [in] */ DWORD dwStartType,
	/* [in] */ DWORD dwErrorControl,
	/* [string][unique][in] */ LPCWSTR lpBinaryPathName,
	/* [string][unique][in] */ LPCWSTR lpLoadOrderGroup,
	/* [partial_ignore][unique][out][in] */ LPDWORD lpdwTagId,
	/* [in] */ DWORD cchDep,
	/* [size_is][unique][in] */ LPCWSTR lpDependencies,
	/* [string][unique][in] */ LPCWSTR lpServiceStartName,
	/* [string][unique][in] */ LPCWSTR lpPassword,
	/* [string][unique][in] */ LPCWSTR lpDisplayName)
{
	BOOL b = ::ChangeServiceConfigW(
		reinterpret_cast<SC_HANDLE>(hService),
		dwServiceType,
		dwStartType,
		dwErrorControl,
		lpBinaryPathName,
		lpLoadOrderGroup,
		lpdwTagId,
		lpDependencies,
		lpServiceStartName,
		lpPassword,
		lpDisplayName);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcChangeServiceConfig2(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ SERVICE_CONFIG_LEVEL dwInfoLevel,
	/* [switch_is][unique][in] */ PSERVICE_CONFIG lpInfo)
{
	BOOL b = ::ChangeServiceConfig2W(
		reinterpret_cast<SC_HANDLE>(hService),
		dwInfoLevel,
		lpInfo);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcCloseServiceHandle(
	/* [out][in] */ SERVICEHANDLE *hSCObject)
{
	BOOL b = ::CloseServiceHandle(reinterpret_cast<SC_HANDLE>(*hSCObject));
	*hSCObject = nullptr;
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcControlService(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ DWORD dwControl,
	/* [out] */ LPSERVICE_STATUS lpServiceStatus)
{
	BOOL b = ::ControlService(
		reinterpret_cast<SC_HANDLE>(hService),
		dwControl,
		lpServiceStatus);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcControlServiceEx(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ DWORD dwControl,
	/* [in] */ DWORD dwInfoLevel,
	/* [out][in] */ PSERVICE_CONTROL_STATUS_REASON_PARAMS pControlParams)
{
	BOOL b = ::ControlServiceExW(
		reinterpret_cast<SC_HANDLE>(hService),
		dwControl,
		dwInfoLevel,
		pControlParams);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcCreateService(
	/* [in] */ SERVICEHANDLE hSCManager,
	/* [string][in] */ LPCWSTR lpServiceName,
	/* [string][unique][in] */ LPCWSTR lpDisplayName,
	/* [in] */ DWORD dwDesiredAccess,
	/* [in] */ DWORD dwServiceType,
	/* [in] */ DWORD dwStartType,
	/* [in] */ DWORD dwErrorControl,
	/* [string][unique][in] */ LPCWSTR lpBinaryPathName,
	/* [string][unique][in] */ LPCWSTR lpLoadOrderGroup,
	/* [partial_ignore][unique][out][in] */ LPDWORD lpdwTagId,
	/* [in] */ DWORD cchDep,
	/* [size_is][unique][in] */ LPCWSTR lpDependencies,
	/* [string][unique][in] */ LPCWSTR lpServiceStartName,
	/* [string][unique][in] */ LPCTSTR lpPassword,
	/* [out] */ SERVICEHANDLE *result)
{
	*reinterpret_cast<SC_HANDLE*>(result) = ::CreateServiceW(
		reinterpret_cast<SC_HANDLE>(hSCManager),
		lpServiceName,
		lpDisplayName,
		dwDesiredAccess,
		dwServiceType,
		dwStartType,
		dwErrorControl,
		lpBinaryPathName,
		lpLoadOrderGroup,
		lpdwTagId,
		lpDependencies,
		lpServiceStartName,
		lpPassword);
	return (*reinterpret_cast<SC_HANDLE*>(result)) ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcDeleteService(
	/* [in] */ SERVICEHANDLE hService)
{
	BOOL b = ::DeleteService(reinterpret_cast<SC_HANDLE>(hService));
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcEnumDependentServices(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ DWORD dwServiceState,
	/* [size_is][partial_ignore][unique][out][in] */ LPBYTE lpServices,
	/* [in] */ DWORD cbBufSize,
	/* [out] */ LPDWORD pcbBytesNeeded,
	/* [out] */ LPDWORD lpServicesReturned)
{
	BOOL b = ::EnumDependentServicesW(
		reinterpret_cast<SC_HANDLE>(hService),
		dwServiceState,
		reinterpret_cast<LPENUM_SERVICE_STATUSW>(lpServices),
		cbBufSize,
		pcbBytesNeeded,
		lpServicesReturned);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcEnumServicesStatus(
	/* [in] */ SERVICEHANDLE hSCManager,
	/* [in] */ DWORD dwServiceType,
	/* [in] */ DWORD dwServiceState,
	/* [size_is][partial_ignore][unique][out][in] */ LPBYTE lpServices,
	/* [in] */ DWORD cbBufSize,
	/* [out] */ LPDWORD pcbBytesNeeded,
	/* [out] */ LPDWORD lpServicesReturned,
	/* [unique][out][in] */ LPDWORD lpResumeHandle)
{
	// So, this function has been superceded by EnumServicesStatusEx, below.
	// It's not present on the phone at all.
	// We'll just call the new version, then...
	BOOL b = ::EnumServicesStatusExW(
		reinterpret_cast<SC_HANDLE>(hSCManager),
		SC_ENUM_PROCESS_INFO,
		dwServiceType,
		dwServiceState,
		lpServices,
		cbBufSize,
		pcbBytesNeeded,
		lpServicesReturned,
		lpResumeHandle,
		nullptr);
	// Convert the array element types.
	// Fortunately, the old type is a strict subset of the new one.
	LPENUM_SERVICE_STATUS_PROCESSW pSSP =
		reinterpret_cast<LPENUM_SERVICE_STATUS_PROCESSW>(lpServices);
	LPENUM_SERVICE_STATUSW pSS = reinterpret_cast<LPENUM_SERVICE_STATUSW>(lpServices);
	// We now have two "arrays" pointing to the same data
	// We can walk forward along the array, starting at the first field of each element
	// We can also skip the first element
	for (DWORD i = 1; i < *lpServicesReturned; i++)
	{	// I feel mildy dirty about pulling silly pointer tricks like this.
		pSS[i].ServiceStatus.dwServiceType = pSSP[i].ServiceStatusProcess.dwServiceType;
		pSS[i].ServiceStatus.dwCurrentState = pSSP[i].ServiceStatusProcess.dwCurrentState;
		pSS[i].ServiceStatus.dwControlsAccepted =
			pSSP[i].ServiceStatusProcess.dwControlsAccepted;
		pSS[i].ServiceStatus.dwWin32ExitCode = 
			pSSP[i].ServiceStatusProcess.dwWin32ExitCode;
		pSS[i].ServiceStatus.dwServiceSpecificExitCode =
			pSSP[i].ServiceStatusProcess.dwServiceSpecificExitCode;
		pSS[i].ServiceStatus.dwCheckPoint = pSSP[i].ServiceStatusProcess.dwCheckPoint;
		pSS[i].ServiceStatus.dwWaitHint = pSSP[i].ServiceStatusProcess.dwWaitHint;
	}
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcEnumServicesStatusEx(
	/* [in] */ SERVICEHANDLE hSCManager,
	/* [in] */ SC_ENUM_TYPE InfoLevel,
	/* [in] */ DWORD dwServiceType,
	/* [in] */ DWORD dwServiceState,
	/* [size_is][partial_ignore][unique][out][in] */ LPBYTE lpServices,
	/* [in] */ DWORD cbBufSize,
	/* [out] */ LPDWORD pcbBytesNeeded,
	/* [out] */ LPDWORD lpServicesReturned,
	/* [unique][out][in] */ LPDWORD lpResumeHandle,
	/* [string][unique][in] */ LPCWSTR pszGroupName)
{
	BOOL b = ::EnumServicesStatusExW(
		reinterpret_cast<SC_HANDLE>(hSCManager),
		InfoLevel,
		dwServiceType,
		dwServiceState,
		lpServices,
		cbBufSize,
		pcbBytesNeeded,
		lpServicesReturned,
		lpResumeHandle,
		pszGroupName);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcGetServiceDisplayName(
	/* [in] */ SERVICEHANDLE hSCManager,
	/* [string][in] */ LPCWSTR lpServiceName,
	/* [size_is][partial_ignore][unique][out][in] */ LPWSTR lpDisplayName,
	/* [in] */ DWORD cchBufferIn,
	/* [out] */ LPDWORD lpcchBufferOut)
{
	//*lpcchBufferOut = cchBufferIn;
	//BOOL b = ::GetServiceDisplayNameW(
	//	reinterpret_cast<SC_HANDLE>(hSCManager),
	//	lpServiceName,
	//	lpDisplayName,
	//	lpcchBufferOut);
	//return b ? NO_ERROR : ::GetLastError();
	return ERROR_CALL_NOT_IMPLEMENTED;
}

WINERR RootRpcGetServiceKeyName(
	/* [in] */ SERVICEHANDLE hSCManager,
	/* [string][in] */ LPCWSTR lpDisplayName,
	/* [size_is][partial_ignore][unique][out][in] */ LPWSTR lpServiceName,
	/* [in] */ DWORD cchBufferIn,
	/* [out] */ LPDWORD lpcchBufferOut)
{
	//*lpcchBufferOut = cchBufferIn;
	//BOOL b = ::GetServiceKeyNameW(
	//	reinterpret_cast<SC_HANDLE>(hSCManager),
	//	lpDisplayName,
	//	lpServiceName,
	//	lpcchBufferOut);
	//return b ? NO_ERROR : ::GetLastError();
	return ERROR_CALL_NOT_IMPLEMENTED;
}

WINERR RootRpcOpenSCManager(
	/* [string][unique][in] */ LPCWSTR lpMachineName,
	/* [string][unique][in] */ LPCWSTR lpDatabaseName,
	/* [in] */ DWORD dwDesiredAccess,
	/* [out] */ SERVICEHANDLE *result) 
{
	*reinterpret_cast<SC_HANDLE*>(result) = 
		::OpenSCManagerW(lpMachineName, lpDatabaseName, dwDesiredAccess);
	return (*reinterpret_cast<SC_HANDLE*>(result)) ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcOpenService(
	/* [in] */ SERVICEHANDLE hSCManager,
	/* [string][in] */ LPCWSTR lpServiceName,
	/* [in] */ DWORD dwDesiredAccess,
	/* [out] */ SERVICEHANDLE *result)
{
	*reinterpret_cast<SC_HANDLE*>(result) = ::OpenServiceW(
		reinterpret_cast<SC_HANDLE>(hSCManager),
		lpServiceName,
		dwDesiredAccess);
	return (*reinterpret_cast<SC_HANDLE*>(result)) ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcQueryServiceConfig(
	/* [in] */ SERVICEHANDLE hService,
	/* [size_is][partial_ignore][unique][out][in] */ LPBYTE lpServiceConfig,
	/* [in] */ DWORD cbBufSize,
	/* [out] */ LPDWORD pcbBytesNeeded)
{
	BOOL b = ::QueryServiceConfigW(
		reinterpret_cast<SC_HANDLE>(hService),
		reinterpret_cast<LPQUERY_SERVICE_CONFIGW>(lpServiceConfig),
		cbBufSize,
		pcbBytesNeeded);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcQueryServiceConfig2(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ SERVICE_CONFIG_LEVEL dwInfoLevel,
	/* [size_is][partial_ignore][unique][out][in] */ LPBYTE lpBuffer,
	/* [in] */ DWORD cbBufSize,
	/* [out] */ LPDWORD pcbBytesNeeded)
{
	BOOL b = ::QueryServiceConfig2W(
		reinterpret_cast<SC_HANDLE>(hService),
		dwInfoLevel,
		lpBuffer,
		cbBufSize,
		pcbBytesNeeded);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcQueryServiceStatus(
	/* [in] */ SERVICEHANDLE hService,
	/* [out] */ LPSERVICE_STATUS lpServiceStatus)
{
	BOOL b = ::QueryServiceStatus(reinterpret_cast<SC_HANDLE>(hService), lpServiceStatus);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcQueryServiceStatusEx(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ SC_STATUS_TYPE InfoLevel,
	/* [size_is][partial_ignore][unique][out][in] */ LPBYTE lpBuffer,
	/* [in] */ DWORD cbBufSize,
	/* [out] */ LPDWORD pcbBytesNeeded)
{
	BOOL b = ::QueryServiceStatusEx(
		reinterpret_cast<SC_HANDLE>(hService),
		InfoLevel,
		lpBuffer,
		cbBufSize,
		pcbBytesNeeded);
	return b ? NO_ERROR : ::GetLastError();
}

WINERR RootRpcStartService(
	/* [in] */ SERVICEHANDLE hService,
	/* [in] */ DWORD dwNumServiceArgs,
	/* [string][size_is][size_is][unique][in] */ LPCWSTR *lpServiceArgVectors)
{
	BOOL b = ::StartServiceW(
		reinterpret_cast<SC_HANDLE>(hService),
		dwNumServiceArgs,
		lpServiceArgVectors);
	return b ? NO_ERROR : ::GetLastError();
}
